package com.xxd.dfs.backupnode;


import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: XiaoDong.Xie
 * @create: 2020-08-14 09:15
 * @description:
 */
public class FSNameSystem {

    /**
     * 操作目录树的组件
     */
    private FSDirectory directory;

    private final static Logger logger = LoggerFactory.getLogger(FSNameSystem.class);

    private long maxTxid = 0L;

    private long checkpointTxid = 0L;

    public FSNameSystem() {
        this.directory = new FSDirectory();
    }

    /**
     * 加载fsimage 和edit log 进来
     *
     * @return
     */
    public String loadFromDisk(Configuration conf) {
        String dirPath = conf.get("fsimage.dir");
        File dir = new File(dirPath);
        if (!dir.exists()) {
            dir.mkdirs();
            return "";
        }

        // 所有文件
        File[] files = dir.listFiles();
        List<File> editLogFile = new ArrayList<>();
        File fsimage = null;

        for (File file : files) {
            String fileName = file.getName();
            if (fileName.startsWith("fsimage")) {
                fsimage = file;
                this.checkpointTxid =
                        Long.parseLong(fileName.split("_")[1].split("\\.")[0]);
                this.maxTxid = checkpointTxid;
            } else {
                editLogFile.add(file);
            }
        }
        loadFSImage(fsimage);
        return fsimage == null ? "" : fsimage.getAbsolutePath();
    }

    /**
     * 加载fsimage
     *
     * @param file
     */
    private void loadFSImage(File file) {
        if (file == null || !file.exists()) {
            logger.info("没有 fsimage 文件.....");
            return;
        }
        logger.info("正在恢复 fsimage 文件 filename = {}", file.getName());
        FileInputStream inputStream = null;
        FileChannel channel = null;
        try {
            inputStream = new FileInputStream(file);
            channel = inputStream.getChannel();

            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1MB 大概能容纳差不多2万多条edit log了
            int count = 0;

            while ((count = channel.read(buffer)) > 0) {
                buffer.flip();
                String fsimageJSON = new String(buffer.array(), 0, count);
                FSDirectory.INode root = JSONObject.parseObject(fsimageJSON, FSDirectory.INode.class);
                directory.setRootDir(root);
                buffer.clear();
            }
        } catch (IOException e) {
            logger.error("fsimage 恢复 出错，退出进程 errorMsg = {}", e);
            System.exit(1);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (channel != null) {
                    channel.close();
                }
            } catch (Exception e) {
                logger.error("error", e);
            }
        }
    }

    /**
     * 创建一个文件目录
     * 1、这是一个绝对路劲
     * 2、不允许创建重复目录
     *
     * @param path 目录
     */
    public void mkdir(String path) throws IOException {
        synchronized (this) {
            directory.mkdir(path);
        }
    }

    public void printTree() {
        directory.printlnTree();
    }

    /**
     * 返回当前的TXid
     *
     * @return
     */
    public long getTxnCount() {
        return maxTxid;
    }

    /**
     * 设置最大maxid
     *
     * @param txid
     */
    public void setMaxTxid(long txid) {
        synchronized (this) {
            this.maxTxid = txid;
        }
    }

    public long getCheckpointTxid() {
        return checkpointTxid;
    }

    public void setCheckpointTxid(long checkpointTxid) {
        this.checkpointTxid = checkpointTxid;
    }

    /**
     * 获取fsimage
     *
     * @return
     */
    public FSImage getFSImage() {
        synchronized (this) {
            String fsImageJSON = directory.getFSImageJSON();
            return new FSImage(maxTxid, fsImageJSON);
        }
    }
}
